#include "key.h"
#include "objectlooper.h"

//default
#define KEY_DELAY_MS 20
#define KEY_DCLICK_MS 500
#define KEY_LONG_MS  1000

#define KEY_TICK_MS 5

static KEY_DOWN_FUNC downEvent = NULL;
static KEY_DOWN_FUNC upEvent = NULL;
static KEY_DOWN_FUNC dClickEvent = NULL;
static KEY_DOWN_FUNC longDownEvent = NULL;

//
static void KeyLoop(key_t *key);

void KeyInit(key_t *key, SOC_GpioPort_t port, uint8_t pin)
{
    key->state = KEY_IDLE;
    key->countMs = 0;   
    key->preVal = 0xff;
    key->hasLongDown = FALSE;
    key->lastClickMsDec = 0;

    key->port = port;
    key->pin = pin;
    key->reverse = FALSE;

    SOC_GpioInit(port , pin , S_GPIO_Mode_IN_PULLUP); 
    key->isInit = TRUE;
    
    RegisterObjectLooper(key, KEY_TICK_MS, KeyLoop); 
}

void KeySetReverse(key_t *key, bool reverse)
{
   key->reverse = reverse;
}

static void KeyLoopImpl(key_t *key, int ms)
{
    int v;

    v = SOC_GpioRead(key->port , key->pin) != 0;
    if(key->reverse)
        v = !v;

    switch(key->state)
    {
    case KEY_IDLE:
        if(key->preVal != 0 && v == 0)
        {
            key->state = KEY_DLY;
            key->countMs = 0;
            key->hasLongDown = FALSE;
        }
    break;
    case KEY_DLY:
        if(key->countMs >= KEY_DELAY_MS)
        {
            if(v == 0)
            {
                if(downEvent != NULL)
                {
                    downEvent(key);
                }

                if(key->lastClickMsDec > 0)
                {
                    if(dClickEvent != NULL)
                    {
                        dClickEvent(key);
                    }
                }

                key->lastClickMsDec = KEY_DCLICK_MS;
                key->state = KEY_DOWN;
            }
            else 
            {
                key->state = KEY_IDLE;
            }
        }
    break;
    case KEY_DOWN:
        if(v == 1)
        {
            if(upEvent != NULL)
            {
                upEvent(key);
            }

            key->countMs = 0;
            key->state = KEY_UP;
            //key->state = KEY_IDLE;
        }
        else
        {
            if(!key->hasLongDown && key->countMs >= KEY_LONG_MS)
            {
                key->hasLongDown = TRUE;
                if(longDownEvent != NULL)
                {
                    longDownEvent(key);
                }
            }
        }
        break;
    case KEY_UP:
        if(key->countMs >= KEY_DELAY_MS)
        {
            key->state = KEY_IDLE;
        }
        break;
    }

    key->countMs += ms;
    key->preVal = v;
    if(key->lastClickMsDec > 0)
    {
        key->lastClickMsDec -= ms;
        if(key->lastClickMsDec < 0)
        {
            key->lastClickMsDec = 0;
        }
    }
}

static void KeyLoop(key_t *key)
{
    if(key->isInit)
        KeyLoopImpl(key, KEY_TICK_MS);
}

void KeySetDownCallBack(KEY_DOWN_FUNC cb)
{
    downEvent = cb;
}

void KeySetUpCallBack(KEY_DOWN_FUNC cb)
{
    upEvent = cb;
}

void KeySetDClickCallBack(KEY_DOWN_FUNC cb)
{
    dClickEvent = cb;
}

void KeySetLongDownCallBack(KEY_DOWN_FUNC cb)
{
    longDownEvent = cb;
}
